﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
namespace DataModel
{
    public class ViewModelBase : INotifyPropertyChanged, INotifyDataErrorInfo, IDataErrorInfo
    {


        /// <summary>
        /// Sets the error message if a condition is met, removes it otherwise.
        /// </summary>
        /// <param name="Condition">True to set the error message</param>
        /// <param name="EM">The error message to set or remove</param>
        /// <returns>The value of the condition</returns>
        protected bool SetErrorMessageIf(bool Condition, ErrorMessage EM)
        {
            if (Condition)
                AddMessage(EM);
            else
                RemoveMessage(EM);
            return Condition;
        }

        protected void ClearErrorMessages()
        {
            List<string> properties = new List<string>();
            foreach (var item in Messages)
                properties.Add(item.propertyName);
            Messages.Clear();
            foreach (var item in properties)
                OnErrorsChanged(item);
        }

        protected void OnPropertyChanged(params string[] PropertyNames)
        {
            foreach (var PropertyName in PropertyNames)
            {
                PropertyChangedMethod(PropertyName, this.GetType().GetProperty(PropertyName).GetGetMethod().Invoke(this, null));
                foreach (var item in PropertyChangedHandlers)
                {
                    try
                    {
                        item.Value.Send((o) =>
                        {
                            item.Key(this, new PropertyChangedEventArgs(PropertyName));
                        }, null);
                    }
                    catch (Exception)
                    {
                        item.Key(this, new PropertyChangedEventArgs(PropertyName));
                    }
                }
            }

        }

        private void AddMessage(ErrorMessage EM)
        {
            if (Messages.Contains(EM))
            {
                Messages.Remove(EM);
                Messages.Add(EM);
            }
            else
            {
                Messages.Add(EM);
            }
            OnErrorsChanged(EM.propertyName);
        }
        private void RemoveMessage(ErrorMessage EM)
        {
            if (Messages.Contains(EM))
            {
                Messages.Remove(EM);
                OnErrorsChanged(EM.propertyName);
            }
        }

        #region INotifyPropertyChanged Members
        protected virtual void PropertyChangedMethod(string PropertyName, object PropertyValue)
        {

        }

        Dictionary<PropertyChangedEventHandler, SynchronizationContext> PropertyChangedHandlers = new Dictionary<PropertyChangedEventHandler, SynchronizationContext>();
        event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
        {
            add
            {
                PropertyChangedHandlers.Add(value, SynchronizationContext.Current);
            }
            remove
            {
                PropertyChangedHandlers.Remove(value);
            }
        }

        #endregion

        #region INotifyDataErrorInfo Members

        private void OnErrorsChanged(string PropertyName)
        {
            var handler = ErrorsChanged;
            if (handler != null)
            {
                handler(this, new DataErrorsChangedEventArgs(PropertyName));
            }
        }

        List<ErrorMessage> Messages = new List<ErrorMessage>();
        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

        System.Collections.IEnumerable INotifyDataErrorInfo.GetErrors(string propertyName)
        {
            return Messages.Where((EM) => EM.propertyName == propertyName).Select((EM) => EM.GetMessage);
        }
        public bool HasErrors
        {
            get { return Messages.Any(); }
        }

        #endregion

        #region IDataErrorInfo Members

        string IDataErrorInfo.Error
        {
            get
            {
                if (!Messages.Any())
                    return "";
                return Messages.Select((EM) => EM.GetMessage).Aggregate((S1, S2) => S1 + Environment.NewLine + S2);
            }
        }

        string IDataErrorInfo.this[string columnName]
        {
            get
            {
                if (!Messages.Any((EM) => EM.propertyName == columnName))
                    return "";
                return Messages.Where((EM) => EM.propertyName == columnName).Select((EM) => EM.GetMessage).Aggregate((S1, S2) => S1 + Environment.NewLine + S2);
            }
        }

        #endregion
    
}
}
